local function print() end

local STACKER	= "stacker_adv"
local STACKER_	= STACKER.."_"

local FREEZE		= "freeze"
local UNFREEZE		= "unfreeze"
local WELD			= "weld"
local NOCOLLIDE		= "nocollide"
local CYCLIC		= "cyclic"
local USEPHYSICS	= "phy"
local WORLDSPACE	= "ws"
local DIRECTION		= "dir"
local COUNT			= "count"
local OFFSET		= "offset"
local OFFTIMESDIM	= "offset_td"
local ROTATION		= "rot"
local ROTORIGIN		= "roto"
local GHOSTTRANSP	= "ghost_transp"
local GHOSTLIM		= "ghost_lim"
local TRIADS		= "triads"

local STACKER_PARAMCHANGED = STACKER_.."paramchanged"


local function GetNum( cvar, min, max )
	local val = GetConVarNumber( STACKER_..cvar )
	if min && max then return math.Clamp( val, min, max ) end
	return val
end
local function SetNum( cvar, num ) RunConsoleCommand( STACKER_..cvar, num ) end
local function GetBool( cvar ) return GetNum( cvar, 0, 1 ) != 0 end
local function SetBool( cvar, state ) SetNum( cvar, state and 1 or 0 ) end

local function SetChangeCallback( cvar, f )
	local clbcks = cvars.GetConVarCallbacks( STACKER_..cvar )
	-- removing dangling callbacks..
	for i, f in ipairs( clbcks or {} ) do clbcks[i] = nil end
	cvars.AddChangeCallback( STACKER_..cvar, f )
end

local function UpdateGhost( cvar, old, new )
	cvar = cvar:gsub("^"..STACKER_, "")
	print("UpdateGhost("..cvar..", "..old..", "..new..")")
	
	local wpn = LocalPlayer():GetActiveWeapon()
	if !IsValid(wpn) || wpn:GetClass() != "gmod_tool" || wpn:GetMode() != STACKER then return end
	wpn:GetToolObject( STACKER ):ParamChanged( cvar, old, new )
	
	-- Send message to server with a delay (so updated client convar will be recieved before the event).
	timer.Simple( 0.1, RunConsoleCommand, STACKER_PARAMCHANGED, cvar, old, new )
end

local function Space() CPanel:AddControl( "Label", { Text = "" } ) end

local function CheckBox( label, cvar, tip )
	cvar = cvar and (STACKER_..cvar) or nil
	local checkbox = CPanel:AddControl( "Checkbox", { Label = label, Command = cvar } )
	if tip then checkbox:SetToolTip( tip ) end
	return checkbox
end

local function VectorSliders( label, cvar, abs )
	
	cvar = STACKER_..cvar
	local lbl = CPanel:AddControl( "Label", { Text = label } )
	CPanel:AddControl( "Slider", { Label = "X",	Type = "float", Min = -abs, Max = abs, Command = cvar.."x" } )
	CPanel:AddControl( "Slider", { Label = "Y",	Type = "float", Min = -abs, Max = abs, Command = cvar.."y" } )
	CPanel:AddControl( "Slider", { Label = "Z",	Type = "float", Min = -abs, Max = abs, Command = cvar.."z" } )
	
	local btn = vgui.Create( "DButton", CPanel )
	btn:SetText( "0" )
	btn:SetToolTip( "Reset" )
	function btn:DoClick()
		RunConsoleCommand( cvar.."x", 0 )
		RunConsoleCommand( cvar.."y", 0 )
		RunConsoleCommand( cvar.."z", 0 )
	end
	return lbl, btn
end

local function AngleSliders( label, cvar, abs )
	
	cvar = STACKER_..cvar
	local lbl = CPanel:AddControl( "Label", { Text = label } )
	CPanel:AddControl( "Slider", { Label = "Pitch",	Type = "float", Min = -abs, Max = abs, Command = cvar.."p" } )
	CPanel:AddControl( "Slider", { Label = "Yaw",	Type = "float", Min = -abs, Max = abs, Command = cvar.."y" } )
	CPanel:AddControl( "Slider", { Label = "Roll",	Type = "float", Min = -abs, Max = abs, Command = cvar.."r" } )
	
	local btn = vgui.Create( "DButton", CPanel )
	btn:SetText( "0" )
	btn:SetToolTip( "Reset" )
	function btn:DoClick()
		RunConsoleCommand( cvar.."p", 0 )
		RunConsoleCommand( cvar.."y", 0 )
		RunConsoleCommand( cvar.."r", 0 )
	end
	return lbl, btn
end


-- Presets -------------
do
	local presets = vgui.Create( "ControlPresets", CPanel )
	presets:SetPreset( STACKER )
	local cvars =
	{
		[FREEZE]		= 0,
		[UNFREEZE]		= 0,
		[WELD]			= 0,
		[NOCOLLIDE]		= 0,
		[CYCLIC]		= 0,
		[WORLDSPACE]	= 0,
		[DIRECTION]		= 1,
		[COUNT]			= 1,
		[USEPHYSICS]	= 0,
		[OFFSET.."x"]	= 0,
		[OFFSET.."y"]	= 0,
		[OFFSET.."z"]	= 0,
		[OFFTIMESDIM]	= 1,
		[ROTATION.."p"]	= 0,
		[ROTATION.."y"]	= 0,
		[ROTATION.."r"]	= 0,
		[ROTORIGIN.."x"]= 0,
		[ROTORIGIN.."y"]= 0,
		[ROTORIGIN.."z"]= 0,
	--	[GHOSTTRANSP]	= 1,
	--	[GHOSTLIM]		= 1,
	--	[TRIADS]		= 0,
	}
	local defaults = {}
	for k, v in pairs(cvars) do
		presets:AddConVar( STACKER_..k )
		defaults[STACKER_..k] = v
	end
	--presets:AddOption( "Default", defaults ) -- bugz o_O
	CPanel:AddItem( presets )
end
------------------------

-- Freeze/Unfreeze -----
local checkbox_frz		= CheckBox( "Freeze" )
local checkbox_unfrz	= CheckBox( "Unfreeze" )

function checkbox_frz.Button:Toggle()
	print( "checkbox_frz.Button:Toggle()" )
	print("\tchecked = "..tostring(self:GetChecked()))
	local checked = !self:GetChecked()
	self:SetValue( checked )
	SetBool( FREEZE, checked )
	if checked then
		checkbox_unfrz:SetValue( false )
		SetBool( UNFREEZE, false )
	end
end
function checkbox_unfrz.Button:Toggle()
	print( "checkbox_unfrz.Button:Toggle()" )
	print("\tchecked = "..tostring(self:GetChecked()))
	local checked = !self:GetChecked()
	self:SetValue( checked )
	SetBool( UNFREEZE, checked )
	if checked then
		checkbox_frz:SetValue( false )
		SetBool( FREEZE, false )
	end
end

local function UpdateFreeze()
	local freeze	= GetBool( FREEZE )
	local unfreeze	= GetBool( UNFREEZE )
	checkbox_frz:SetValue( freeze && !unfreeze )
	checkbox_unfrz:SetValue( unfreeze )
end

UpdateFreeze()
SetChangeCallback( FREEZE,   function(cvar, old, new) print("freeze -> "..new)   UpdateFreeze() end )
SetChangeCallback( UNFREEZE, function(cvar, old, new) print("unfreeze -> "..new) UpdateFreeze() end )
------------------------

CheckBox( "Weld", WELD )
CheckBox( "No-collide", NOCOLLIDE )
CheckBox( "Cyclic", CYCLIC, "Weld/no-collide tail whith head." )

-- Mode ----------------
Space()
local lbl_relativeto = CPanel:AddControl( "Label", { Text = "Relative to:" } )

local checkbox_ent		= CheckBox( "Entity" )
local checkbox_world	= CheckBox( "World", nil, "Buggy!" )

checkbox_ent.Button:SetTextInset(0,1)
checkbox_world.Button:SetTextInset(0,1)

function checkbox_ent.Button:Toggle()
	local checked = !self:GetChecked()
	if !checked then return end
	self:SetValue( checked )
	checkbox_world:SetValue( !checked )
	SetBool( WORLDSPACE, !checked )
	
	if checked then
		self:SetType( "dot" )
		self:SetChar( "h" )
	else
		self:SetType( "none" )
		self:SetChar( "" )
	end
end
function checkbox_world.Button:Toggle()
	local checked = !self:GetChecked()
	if !checked then return end
	self:SetValue( checked )
	checkbox_ent:SetValue( !checked )
	SetBool( WORLDSPACE, checked )
	
	if checked then
		self:SetType( "dot" )
		self:SetChar( "h" )
	else
		self:SetType( "none" )
		self:SetChar( "" )
	end
end

local function UpdateMode()
	local val = GetBool( WORLDSPACE )
	checkbox_world:SetValue( val )
	checkbox_world.Button:SetType( val and "dot" or "none" )
	checkbox_world.Button:SetChar( val and "h" or "" )
	checkbox_ent:SetValue( !val )
	checkbox_ent.Button:SetType( !val and "dot" or "none" )
	checkbox_ent.Button:SetChar( !val and "h" or "" )
end

UpdateMode()
SetChangeCallback( WORLDSPACE, function(cvar, old, new) print("ws -> "..new) UpdateMode() end )
------------------------

-- Direction -----------
local Buttons = {}
local Labels = "tutu43" --"^v^v><"
local Tips = { "Up", "Down", "Front", "Behind", "Right", "Left" }
local SelectedButton

local function UpdateDir( num )
	print("UpdateDir(".. tostring(num) .."):")
	num = num or GetNum( DIRECTION, 0, 6 )
	print("\tnum = "..num)
	if SelectedButton then SelectedButton:SetSelected( false ) end
	if num == 0 then SelectedButton = nil return end
	SelectedButton = Buttons[num]
	SelectedButton:SetSelected( true )
end

for i = 1, 6 do
	local btn = vgui.Create( "DSysButton", CPanel )
	btn:SetText( Labels:sub(i,i) )
	btn:SetToolTip( Tips[i] )
	
	function btn:DoClick()
		local num = self:GetSelected() and 0 or i
		SetNum( DIRECTION, num )
		UpdateDir( num )
	end
	
	-- Do not insert all 6 buttons, because we actually taking half of that space..
	if i <= 3 then CPanel:AddPanel( btn ) end
	Buttons[i] = btn
end

UpdateDir()
SetChangeCallback( DIRECTION, function(cvar, old, new) print("dir -> "..new) UpdateDir() end )
------------------------

CPanel:AddControl( "Slider", { Label = "Count", Type = "integer", Min = 1, Max = 10, Command = STACKER_..COUNT } )
SetChangeCallback( COUNT, UpdateGhost )
CheckBox( "Use actual boundaries", USEPHYSICS, "Use physics bounding box instead of entity b-box.\n" ..
	"For technical reasons prop and ragdoll previews will be disabled." )
SetChangeCallback( USEPHYSICS, UpdateGhost )

-- Sliders -------------
Space()
local lbl_offset, btn_offset = VectorSliders( "Offset", OFFSET, 256 )
CheckBox( "Times entity dimensions", OFFTIMESDIM, "Use entity dimensions as units for offset" )
Space()
local lbl_rot, btn_rot		= AngleSliders( "Rotation", ROTATION, 180 )
Space()
local lbl_roto, btn_roto	= VectorSliders( "Rotation Origin", ROTORIGIN, 256 )
------------------------

-- Preview -------------
Space()
CPanel:AddControl( "Label", { Text = "Preview Options" } )
CheckBox( "Transparent", GHOSTTRANSP )
CheckBox( "Limited", GHOSTLIM )
CheckBox( "Show triads", TRIADS )
SetChangeCallback( GHOSTTRANSP, UpdateGhost )
SetChangeCallback( GHOSTLIM, UpdateGhost )
------------------------


if !CPanel.OriginalPerformLayout then
	CPanel.OriginalPerformLayout = CPanel.PerformLayout
end
function CPanel:PerformLayout( ... )
	self:OriginalPerformLayout( ... )
	
	local size = Buttons[1]:GetTall()
	
	Buttons[1]:SetWide( size )
	Buttons[2]:CopyBounds( Buttons[1] )
	Buttons[3]:CopyBounds( Buttons[1] )
	Buttons[4]:CopyBounds( Buttons[1] )
	Buttons[5]:CopyBounds( Buttons[1] )
	Buttons[6]:CopyBounds( Buttons[1] )
	
	Buttons[1]:CenterHorizontal()
	Buttons[2]:CenterHorizontal()
	Buttons[2]:MoveBelow( Buttons[1], size )
	
	Buttons[5]:MoveBelow( Buttons[1] )
	Buttons[5]:MoveRightOf( Buttons[1] )
	Buttons[6]:MoveBelow( Buttons[1] )
	Buttons[6]:MoveLeftOf( Buttons[1] )
	
	Buttons[3]:SetTall( math.ceil(size*2/5) )
	Buttons[3]:CenterHorizontal()
	Buttons[3]:MoveBelow( Buttons[1] )
	
	Buttons[4]:SetTall( size - math.ceil(size*2/5) )
	Buttons[4]:CenterHorizontal()
	Buttons[4]:MoveBelow( Buttons[3] )
	
	
	local size = lbl_offset:GetTall() * 1.5
	
	btn_offset:SetSize( size, size )
	btn_offset:SetPos( lbl_offset:GetPos() )
	btn_offset:CenterHorizontal()
	
	btn_rot:SetSize( size, size )
	btn_rot:SetPos( lbl_rot:GetPos() )
	btn_rot:CenterHorizontal()
	
	btn_roto:SetSize( size, size )
	btn_roto:SetPos( lbl_roto:GetPos() )
	btn_roto:CenterHorizontal()
	
	Buttons[1]:SetTextInset(0)
	Buttons[2]:SetTextInset(0)
	Buttons[3]:SetTextInset(0)
	Buttons[4]:SetTextInset(0)
	Buttons[5]:SetTextInset(0)
	Buttons[6]:SetTextInset(0)
end
